#include "Source.h"
#include <random>
#include <assert.h>

// *** *** RandomSource *** **** //

RandomSource::RandomSource(int totalValues, float magnitude) {
	_totalValues = totalValues;
	_generatedValues = 0;
	_magnitude = magnitude;
}

bool RandomSource::HasValues() {
	return _generatedValues < _totalValues;
}

float RandomSource::Next() {
	if(_magnitude <= 0)
		throw "Magnitude must be positive.";
	if(!HasValues())
		throw "No values left.";
	float result = (rand()/((float)RAND_MAX)) * _magnitude;
	_generatedValues++;
	return result;
}


// *** *** VIReader *** **** //

VIReader::VIReader(istream& in, VIReader::mode_t mode) : _in(in) {
	if(!_in.good() || _in.eof())
		throw "Bad input stream.";
	_initialised = false;
	_recordIndex = (mode == mode_t::Voltage) ? 0 : 1;
	_blockTotal = 0;
	_blockCount = 0;
	_generatedValues = 0;
}

void VIReader::TestNotEof() {
	if(_in.eof())
		throw "Bad input format.";
}

unsigned long VIReader::ReadLittleEndian32(istream& in) {
	unsigned long result = 0;
	char ch;
	unsigned char byte;
	int p = in.tellg();
	assert(p % 4 == 0);
	for(int i=0; i<4; i++) {
		in.read(&ch, 1);
		byte = *((unsigned char*) ((void*)&ch));
		result |=  byte << (i * 8);
	}
	return result;
}

void VIReader::ReadBlockSize() {
	_blockCount = 0;
	do {
		_blockTotal = ReadLittleEndian32(_in);
	}
	while(_blockTotal <= 0 && !_in.eof());  // If block size = 0, skip to next block	
	if(_in.eof())
		_blockTotal = 0;  // For the test in HasValues()
}

void VIReader::ReadHeader() {
	TestNotEof();
	_in.seekg(16, _in.beg);  // Skip hash
	ReadBlockSize();
	_initialised = true;
}

bool VIReader::HasValues() {
	if(!_initialised)
		ReadHeader();
	return (_blockCount < _blockTotal);  // We ensure this works by updating _blockCount in Next()
}

float VIReader::Next() {
	if(!_initialised)
		ReadHeader();
	if(HasValues())
		TestNotEof();
	float values[RECORD_SIZE];
	for(int i=0; i<RECORD_SIZE; i++) {
		// TODO: Read entire blocks at once, for performance.
		unsigned long rawValue = ReadLittleEndian32(_in);
		void* pRaw = (void*) &rawValue;
		values[i] = *((float*) pRaw);
	}
	_blockCount++;
	_generatedValues++;
	if(_blockCount >= _blockTotal)
		if(_in.eof())
			_blockTotal = 0;  // For the test in HasValues()
		else
			ReadBlockSize();
	return values[_recordIndex];
}